﻿namespace OasisCiq.Xml
{
    using System;
    using System.Xml;
    using Xpil;

    public class XpilXmlWriter
    {
        private const string XmlNS = XmlNamespaces.Party;
        private readonly XmlWriter writer;
        private readonly XnlXmlWriter xnlWriter;
        private readonly XalXmlWriter xalWriter;
        private readonly CommonTypesXmlWriter ctWriter;
        private readonly XLinkXmlWriter xLinkWriter;

        public XpilXmlWriter(XmlWriter writer)
        {
            if (writer == null)
            {
                throw new ArgumentNullException();
            }

            this.writer = writer;
            ctWriter = new CommonTypesXmlWriter(writer);
            xnlWriter = new XnlXmlWriter(writer);
            xalWriter = new XalXmlWriter(writer);
            xLinkWriter = new XLinkXmlWriter(writer);
        }

        public void WriteAccounts(Accounts element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Accounts", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var account in element)
            {
                WriteAccount(account);
            }

            writer.WriteEndElement();
        }

        public void WriteAddresses(Addresses element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Addresses", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var address in element)
            {
                xalWriter.WriteAddress(address, "Address", XmlNS);
            }

            writer.WriteEndElement();
        }

        public void WriteAccount(Account element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Account", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.OwnershipType != null)
            {
                writer.WriteAttributeString("OwnershipType", XmlNS, element.OwnershipType.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            if (element.AccountElements != null)
            {
                foreach (var accountElement in element.AccountElements)
                {
                    WriteAccountElement(accountElement);
                }
            }

            xnlWriter.WriteOrganisationName(element.Organization, "Organisation", XmlNS);
            writer.WriteEndElement();
        }

        public void WriteAccountElement(AccountElement element)
        {
            if (element == null && element.Value != null)
            {
                return;
            }

            writer.WriteStartElement("AccountElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement();
        }

        public void WriteBirthInfo(BirthInfo element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("BirthInfo", XmlNS);
            if (element.BirthDateTime != null)
            {
                writer.WriteAttributeString("BirthDateTime", XmlNS, element.BirthDateTime.ToString());
            }

            if (element.BirthDateTimePrecision != null)
            {
                writer.WriteAttributeString("BirthDateTimePrecision", XmlNS, element.BirthDateTimePrecision.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var birthInfoElement in element)
            {
                WriteBirthInfoElement(birthInfoElement);
            }

            if (element.BirthPlaceDetails != null)
            {
                xalWriter.WriteAddress(element.BirthPlaceDetails, "BirthPlaceDetails", XmlNS);
            }

            writer.WriteEndElement(); // p:BirthInfo
        }

        public void WriteBirthInfoElement(BirthInfoElement element)
        {
            if (element == null && element.Value != null)
            {
                return;
            }

            writer.WriteStartElement("BirthInfoElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:BirthInfoElement
        }

        public void WriteContactNumbers(ContactNumbers element)
        {
            if (element == null || element.Count == 0) // at least p:ContactNumber required
            {
                return;
            }

            writer.WriteStartElement("ContactNumbers", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var contactNumber in element)
            {
                WriteContactNumber(contactNumber);
            }

            writer.WriteEndElement(); // p:ContactNumbers
        }

        public void WriteContactNumber(ContactNumber element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("ContactNumber", XmlNS);
            if (element.CommunicationMediaType != null)
            {
                writer.WriteAttributeString("CommunicationMediaType", XmlNS, element.CommunicationMediaType.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            if (element.Usage != null)
            {
                writer.WriteAttributeString("Usage", XmlNS, element.Usage.ToString());
            }

            if (element.ContactHours != null)
            {
                writer.WriteAttributeString("ContactHours", XmlNS, element.ContactHours.ToString());
            }

            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var contactNumberElement in element)
            {
                WriteContactNumberElement(contactNumberElement);
            }

            writer.WriteEndElement(); // p:ContactNumber
        }

        public void WriteContactNumberElement(ContactNumberElement element)
        {
            if (element == null && element.Value != null)
            {
                return;
            }

            writer.WriteStartElement("ContactNumberElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:ContactNumberElement
        }

        public void WriteCountryOfResidence(CountryOfResidence element)
        {
            if (element == null)
            {
                return;
            }

            xalWriter.WriteCountryStartElement(element, "Country", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.Status != null)
            {
                writer.WriteAttributeString("Status", XmlNS, element.Status.ToString());
            }

            ctWriter.WriteValidityDateGroup(element);
            xalWriter.WriteCountryEndElement(element);
        }

        public void WriteCountriesOfResidence(CountriesOfResidence element)
        {
            if (element == null || element.Count == 0) // at least p:Country required
            {
                return;
            }

            writer.WriteStartElement("CountriesOfResidence", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var country in element)
            {
                WriteCountryOfResidence(country);
            }

            writer.WriteEndElement(); // p:CountriesOfResidence
        }

        public void WriteDocumentElement(DocumentElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("DocumentElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:DocumentElement
        }

        public void WriteDocument(Document element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Document", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var documentElement in element)
            {
                WriteDocumentElement(documentElement);
            }

            xnlWriter.WritePartyName(element.NameOnDocument, "NameOnDocument", XmlNS);
            xalWriter.WriteAddress(element.AddressOnDocument, "AddressOnDocument", XmlNS);
            xnlWriter.WriteOrganisationName(element.IssuerName, "IssuerName", XmlNS);
            writer.WriteEndElement(); // p:Document
        }

        public void WriteDocuments(Documents element)
        {
            if (element == null || element.Count == 0) // at least one p:Document required
            {
                return;
            }

            writer.WriteStartElement("Documents", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var document in element)
            {
                WriteDocument(document);
            }

            writer.WriteEndElement(); // p:Documents
        }

        public void WriteElectronicAddressIdentifier(ElectronicAddressIdentifier element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("ElectronicAddressIdentifier", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.Status != null)
            {
                writer.WriteAttributeString("Status", XmlNS, element.Status.ToString());
            }

            if (element.Usage != null)
            {
                writer.WriteAttributeString("Usage", XmlNS, element.Usage.ToString());
            }

            if (element.Label != null)
            {
                writer.WriteAttributeString("Label", XmlNS, element.Label.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:ElectronicAddressIdentifier
        }

        public void WriteElectronicAddressIdentifiers(ElectronicAddressIdentifiers element)
        {
            if (element == null || element.Count == 0) // at least one identifier required
            {
                return;
            }

            writer.WriteStartElement("ElectronicAddressIdentifiers", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var electronicAddressIdentifier in element)
            {
                WriteElectronicAddressIdentifier(electronicAddressIdentifier);
            }

            writer.WriteEndElement(); // p:ElectronicAddressIdentifiers
        }

        public void WriteEvent(Event element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Event", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteValidityDateGroup(element);
            if (element.Date != null)
            {
                writer.WriteAttributeString("Date", XmlNS, element.Date.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Event
        }

        public void WriteEvents(Events element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Events", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var @event in element)
            {
                WriteEvent(@event);
            }

            writer.WriteEndElement(); // p:Events
        }

        public void WriteFavourite(Favourite element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Favourite", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Favourite
        }

        public void WriteFavourites(Favourites element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Favourites", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var favourite in element)
            {
                WriteFavourite(favourite);
            }

            writer.WriteEndElement(); // p:Favourites
        }

        public void WriteFreeTextLine(FreeTextLine element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("FreeTextLine", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:FreeTextLine
        }

        public void WriteFreeTextLines(FreeTextLines element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("FreeTextLines", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var textLine in element)
            {
                WriteFreeTextLine(textLine);
            }

            writer.WriteEndElement(); // p:FreeTextLines
        }

        public void WriteHabit(Habit element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Habit", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Habit
        }

        public void WriteHabits(Habits element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Habits", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var habit in element)
            {
                WriteHabit(habit);
            }

            writer.WriteEndElement(); // p:Habits
        }

        public void WriteHobby(Hobby element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Hobby", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Hobby
        }

        public void WriteHobbies(Hobbies element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Hobbies", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var hobby in element)
            {
                WriteHobby(hobby);
            }

            writer.WriteEndElement(); // p:Hobbies
        }

        public void WriteIdentifierElement(IdentifierElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("IdentifierElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:IdentifierElement
        }

        public void WriteIdentifier(Identifier element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Identifier", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var idElement in element)
            {
                WriteIdentifierElement(idElement);
            }

            xnlWriter.WriteOrganisationName(element.IssuerName, "IssuerName", XmlNS);
            writer.WriteEndElement(); // p:Identifier
        }

        public void WriteIdentifiers(Identifiers element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Identifiers", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var id in element)
            {
                WriteIdentifier(id);
            }

            writer.WriteEndElement(); // p:Identifiers
        }

        public void WriteLanguage(Language element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Language", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.Speak != null)
            {
                writer.WriteAttributeString("Speak", XmlNS, element.Speak.ToString());
            }

            if (element.Read != null)
            {
                writer.WriteAttributeString("Read", XmlNS, element.Read.ToString());
            }

            if (element.Write != null)
            {
                writer.WriteAttributeString("Write", XmlNS, element.Write.ToString());
            }

            if (element.Understand != null)
            {
                writer.WriteAttributeString("Understand", XmlNS, element.Understand.ToString());
            }

            if (element.Preference != null)
            {
                writer.WriteAttributeString("Preference", XmlNS, element.Preference.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Language
        }

        public void WriteLanguages(Languages element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Languages", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var lang in element)
            {
                WriteLanguage(lang);
            }

            writer.WriteEndElement(); // p:Languages
        }

        public void WriteMembershipElement(MembershipElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("MembershipElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:MembershipElement
        }

        public void WriteMembership(Membership element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Membership", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var membershipElement in element)
            {
                WriteMembershipElement(membershipElement);
            }

            xnlWriter.WriteOrganisationName(element.Organisation, "Organisation", XmlNS);
            writer.WriteEndElement(); // p:Membership
        }

        public void WriteMemberships(Memberships element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Memberships", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var membership in element)
            {
                WriteMembership(membership);
            }

            writer.WriteEndElement(); // p:Memberships
        }

        public void WriteNationalityCountry(NationalityCountry element)
        {
            if (element == null)
            {
                return;
            }

            xalWriter.WriteCountryStartElement(element, "Country", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }
            
            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            xalWriter.WriteCountryEndElement(element); // p:Country
        }

        public void WriteNationalities(Nationalities element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Nationalities", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var country in element)
            {
                WriteNationalityCountry(country);
            }

            writer.WriteEndElement(); // p:Nationalities
        }

        public void WriteOccupationElement(OccupationElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("OccupationElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:OccupationElement
        }

        public void WriteOccupation(Occupation element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Occupation", XmlNS);
            if (element.IsSelfEmployed != null)
            {
                writer.WriteAttributeString("isSelfEmployed", XmlNS, element.IsSelfEmployed.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var occupationElement in element)
            {
                WriteOccupationElement(occupationElement);
            }

            xnlWriter.WriteOrganisationName(element.Employer, "Employer", XmlNS);
            writer.WriteEndElement(); // p:Occupation
        }

        public void WriteOccupations(Occupations element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Occupations", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var occupation in element)
            {
                WriteOccupation(occupation);
            }

            writer.WriteEndElement(); // p:Occupations
        }

        public void WriteOrganisationInfo(OrganisationInfo element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("OrganisationInfo", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.CategoryType != null)
            {
                writer.WriteAttributeString("CategoryType", XmlNS, element.CategoryType.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            if (element.Nature != null)
            {
                writer.WriteAttributeString("Nature", XmlNS, element.Nature.ToString());
            }

            if (element.IndustryType != null)
            {
                writer.WriteAttributeString("IndustryType", XmlNS, element.IndustryType.ToString());
            }

            if (element.IndustryCode != null)
            {
                writer.WriteAttributeString("IndustryCode", XmlNS, element.IndustryCode.ToString());
            }

            if (element.IndustryCodeType != null)
            {
                writer.WriteAttributeString("IndustryCodeType", XmlNS, element.IndustryCodeType.ToString());
            }

            if (element.NumberOfEmployees != null)
            {
                writer.WriteAttributeString("NumberOfEmployees", XmlNS, element.NumberOfEmployees.ToString());
            }

            if (element.OperatingHourStartTime != null)
            {
                writer.WriteAttributeString("OperatingHourStartTime", XmlNS, element.OperatingHourStartTime.ToString());
            }

            if (element.OperatingHourEndTime != null)
            {
                writer.WriteAttributeString("OperatingHourEndTime", XmlNS, element.OperatingHourEndTime.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteEndElement(); // p:OrganisationInfo
        }

        public void WritePersonInfo(PersonInfo element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("PersonInfo", XmlNS);
            if (element.Age != null)
            {
                writer.WriteAttributeString("Age", XmlNS, element.Age.ToString());
            }

            if (element.CategoryType != null)
            {
                writer.WriteAttributeString("CategoryType", XmlNS, element.CategoryType.ToString());
            }

            if (element.PhysicalStatus != null)
            {
                writer.WriteAttributeString("PhysicalStatus", XmlNS, element.PhysicalStatus.ToString());
            }

            if (element.MaritalStatus != null)
            {
                writer.WriteAttributeString("MaritalStatus", XmlNS, element.MaritalStatus.ToString());
            }

            if (element.Ethnicity != null)
            {
                writer.WriteAttributeString("Ethnicity", XmlNS, element.Ethnicity.ToString());
            }

            if (element.Gender != null)
            {
                writer.WriteAttributeString("Gender", XmlNS, element.Gender.ToString());
            }

            if (element.Religion != null)
            {
                writer.WriteAttributeString("Religion", XmlNS, element.Religion.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteEndElement(); // p:PersonInfo
        }

        public void WritePhysicalInfoFreeTextLine(PhysicalInfoFreeTextLine element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("FreeTextLine", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:FreeTextLine
        }

        public void WritePhysicalFeature(PhysicalFeature element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Feature", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.UnitType != null)
            {
                writer.WriteAttributeString("UnitType", XmlNS, element.UnitType.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Feature
        }

        public void WriteBodyMark(BodyMark element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("BodyMark", XmlNS);
            if (element.Part != null)
            {
                writer.WriteAttributeString("Part", XmlNS, element.Part.ToString());
            }

            if (element.Location != null)
            {
                writer.WriteAttributeString("Location", XmlNS, element.Location.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:BodyMark
        }

        public void WriteDisability(Disability element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Disability", XmlNS);
            if (element.Cause != null)
            {
                writer.WriteAttributeString("Cause", XmlNS, element.Cause.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Disability
        }

        public void WriteAllergy(Allergy element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Allergy", XmlNS);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Allergy
        }

        public void WriteHealthCondition(HealthCondition element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("HealthCondition", XmlNS);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:HealthCondition
        }

        public void WritePhysicalInfo(PhysicalInfo element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("PhysicalInfo", XmlNS);
            if (element.BloodGroup != null)
            {
                writer.WriteAttributeString("BloodGroup", XmlNS, element.BloodGroup.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            if (element.FreeTextLines != null)
            {
                foreach (var freeTextLine in element.FreeTextLines)
                {
                    WritePhysicalInfoFreeTextLine(freeTextLine);
                }
            }

            if (element.Features != null)
            {
                foreach (var feature in element.Features)
                {
                    WritePhysicalFeature(feature);
                }
            }

            if (element.BodyMarks != null)
            {
                foreach (var bodyMark in element.BodyMarks)
                {
                    WriteBodyMark(bodyMark);
                }
            }

            if (element.Disabilities != null)
            {
                foreach (var disability in element.Disabilities)
                {
                    WriteDisability(disability);
                }
            }

            if (element.Allergies != null)
            {
                foreach (var allergy in element.Allergies)
                {
                    WriteAllergy(allergy);
                }
            }

            if (element.HealthConditions != null)
            {
                foreach (var healthConditions in element.HealthConditions)
                {
                    WriteHealthCondition(healthConditions);
                }
            }

            writer.WriteEndElement(); // p:PhysicalInfo
        }

        public void WritePersonPreference(PersonPreference element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("Preference", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:Preference
        }

        public void WritePersonPreferences(PersonPreferences element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Preferences", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var preference in element)
            {
                WritePersonPreference(preference);
            }

            writer.WriteEndElement(); // p:Preferences
        }

        public void WriteQualificationElement(QualificationElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("QualificationElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:QualificationElement
        }

        public void WriteQualification(Qualification element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Qualification", XmlNS);
            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var qualificationElement in element)
            {
                WriteQualificationElement(qualificationElement);
            }

            xnlWriter.WriteOrganisationName(element.Institution, "Institution", XmlNS);
            writer.WriteEndElement(); // p:Qualification
        }

        public void WriteQualifications(Qualifications element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Qualifications", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var qualification in element)
            {
                WriteQualification(qualification);
            }

            writer.WriteEndElement(); // p:Qualifications
        }

        public void WriteRelationship(Relationship element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Relationship", XmlNS);
            ctWriter.WriteStatusList(element.Status, XmlNS);
            if (element.PartyType != null)
            {
                writer.WriteAttributeString("PartyType", XmlNS, element.PartyType.ToString());
            }

            if (element.RelationshipWithPerson != null)
            {
                writer.WriteAttributeString("RelationshipWithPerson", XmlNS, element.RelationshipWithPerson.ToString());
            }

            if (element.RelationshipWithOrganisation != null)
            {
                writer.WriteAttributeString("RelationshipWithOrganisation", XmlNS, element.RelationshipWithOrganisation.ToString());
            }

            if (element.RelationshipValidFrom != null)
            {
                writer.WriteAttributeString("RelationshipValidFrom", XmlNS, element.RelationshipValidFrom.ToString());
            }

            if (element.RelationshipValidTo != null)
            {
                writer.WriteAttributeString("RelationshipValidTo", XmlNS, element.RelationshipValidTo.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            xnlWriter.WriteNameLine(element.NameLine);
            xnlWriter.WritePersonName(element.PersonName);
            xnlWriter.WriteOrganisationName(element.OrganisationName);
            WriteAddresses(element.Addresses);
            WriteContactNumbers(element.ContactNumbers);
            WriteElectronicAddressIdentifiers(element.ElectronicAddressIdentifiers);
            writer.WriteEndElement(); // p:Relationship
        }

        public void WriteRelationships(Relationships element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Relationships", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var relationship in element)
            {
                WriteRelationship(relationship);
            }

            writer.WriteEndElement(); // p:Relationships
        }

        public void WriteRevenue(Revenue element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Revenue", XmlNS);
            if (element.CurrencyCode != null)
            {
                writer.WriteAttributeString("CurrencyCode", XmlNS, element.CurrencyCode.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            if (element.PeriodFrom != null)
            {
                writer.WriteAttributeString("PeriodFrom", XmlNS, element.PeriodFrom.ToString());
            }

            if (element.PeriodTo != null)
            {
                writer.WriteAttributeString("PeriodTo", XmlNS, element.PeriodTo.ToString());
            }

            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.Precision != null)
            {
                writer.WriteAttributeString("Precision", XmlNS, element.Precision.ToString());
            }

            if (element.Source != null)
            {
                writer.WriteAttributeString("Source", XmlNS, element.Source.ToString());
            }

            if (element.CountryName != null)
            {
                writer.WriteAttributeString("CountryName", XmlNS, element.CountryName.ToString());
            }

            if (element.AfterTax != null)
            {
                writer.WriteAttributeString("AfterTax", XmlNS, element.AfterTax.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement(); // p:Revenue
        }

        public void WriteRevenues(Revenues element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Revenues", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var revenue in element)
            {
                WriteRevenue(revenue);
            }

            writer.WriteEndElement(); // p:Revenues
        }

        public void WriteStock(Stock element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Stock", XmlNS);
            if (element.ListedCode != null)
            {
                writer.WriteAttributeString("ListedCode", XmlNS, element.ListedCode.ToString());
            }

            if (element.MarketName != null)
            {
                writer.WriteAttributeString("MarketName", XmlNS, element.MarketName.ToString());
            }

            if (element.CountryName != null)
            {
                writer.WriteAttributeString("CountryName", XmlNS, element.CountryName.ToString());
            }

            if (element.InvestedDate != null)
            {
                writer.WriteAttributeString("InvestedDate", XmlNS, element.InvestedDate.ToString());
            }

            if (element.ShareQuantity != null)
            {
                writer.WriteAttributeString("ShareQuantity", XmlNS, element.ShareQuantity.ToString());
            }

            if (element.ListedDate != null)
            {
                writer.WriteAttributeString("ListedDate", XmlNS, element.ListedDate.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteEndElement(); // p:Stock
        }

        public void WriteStocks(Stocks element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Stocks", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var stock in element)
            {
                WriteStock(stock);
            }

            writer.WriteEndElement(); // p:Stocks
        }

        public void WriteVehicleElement(VehicleElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("VehicleElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:VehicleElement
        }

        public void WriteVehicle(Vehicle element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Vehicle", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var vehicleElement in element)
            {
                WriteVehicleElement(vehicleElement);
            }

            writer.WriteEndElement(); // p:Vehicle
        }

        public void WriteVehicles(Vehicles element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Vehicles", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var vehicle in element)
            {
                WriteVehicle(vehicle);
            }

            writer.WriteEndElement(); // p:Vehicles
        }

        public void WriteVisaElement(VisaElement element)
        {
            if (element == null || element.Value == null)
            {
                return;
            }

            writer.WriteStartElement("VisaElement", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.Value);
            writer.WriteEndElement(); // p:VisaElement
        }

        public void WriteVisa(Visa element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Visa", XmlNS);
            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            if (element.EntryByDate != null)
            {
                writer.WriteAttributeString("EntryByDate", XmlNS, element.EntryByDate.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var visaElement in element)
            {
                WriteVisaElement(visaElement);
            }

            writer.WriteEndElement(); // p:Visa
        }

        public void WriteVisas(Visas element)
        {
            if (element == null || element.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("Visas", XmlNS);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var visa in element)
            {
                WriteVisa(visa);
            }

            writer.WriteEndElement(); // p:Visas
        }

        /// <summary>
        /// Note at least one organisation name required
        /// </summary>
        public void WriteOrganisationDetails(OrganisationDetailsType element)
        {
            if (element == null || element.OrganisationNames == null ||
                element.OrganisationNames.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("OrganisationDetails", XmlNS);
            if (element.Usage != null)
            {
                writer.WriteAttributeString("Usage", XmlNS, element.Usage.ToString());
            }

            if (element.Status != null)
            {
                writer.WriteAttributeString("Status", XmlNS, element.Status.ToString());
            }

            ctWriter.WriteValidityDateGroup(element);
            if (element.OrganisationDetailsKey != null)
            {
                writer.WriteAttributeString(
                    "OrganisationDetailsKey",
                    XmlNS,
                    element.OrganisationDetailsKey.ToString());
            }
            
            if (element.OrganisationDetailsKeyRef != null)
            {
                writer.WriteAttributeString(
                    "OrganisationDetailsKeyRef",
                    XmlNS,
                    element.OrganisationDetailsKeyRef.ToString());
            }

            xLinkWriter.WriteXLink(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteLanguageCodeGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            WriteFreeTextLines(element.FreeTextLines);
            if (element.OrganisationNames != null)
            {
                foreach (var organisationName in element.OrganisationNames)
                {
                    xnlWriter.WriteOrganisationName(organisationName);
                }
            }

            WriteAddresses(element.Addresses);
            WriteAccounts(element.Accounts);
            WriteContactNumbers(element.ContactNumbers);
            WriteDocuments(element.Documents);
            WriteElectronicAddressIdentifiers(element.ElectronicAddressIdentifiers);
            WriteEvents(element.Events);
            WriteIdentifiers(element.Identifiers);
            WriteMemberships(element.Memberships);
            WriteRelationships(element.Relationships);
            WriteRevenues(element.Revenues);
            WriteStocks(element.Stocks);
            WriteVehicles(element.Vehicles);
            WriteOrganisationInfo(element.OrganisationInfo);
            writer.WriteEndElement();
        }

        public void WriteParty(PartyType element)
        {
            WritePartyStartElement(element, "Party", XmlNS);
            WritePartyChildren(element);
            WritePartyEndElement(element);
        }

        public void WritePartyStartElement(PartyType element, string elementName, string elementNamespace)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement(elementName, elementNamespace);
            if (element.Type != null)
            {
                writer.WriteAttributeString("PartyType", XmlNS, element.Type.ToString());
            }

            if (element.PartyId != null)
            {
                writer.WriteAttributeString("PartyID", XmlNS, element.PartyId.ToString());
            }

            if (element.PartyIdType != null)
            {
                writer.WriteAttributeString("PartyIDType", XmlNS, element.PartyIdType.ToString());
            }

            if (element.Id != null)
            {
                writer.WriteAttributeString("ID", XmlNS, element.Id.ToString());
            }

            if (element.Usage != null)
            {
                writer.WriteAttributeString("Usage", XmlNS, element.Usage.ToString());
            }

            if (element.Status != null)
            {
                writer.WriteAttributeString("Status", XmlNS, element.Status.ToString());
            }

            ctWriter.WriteValidityDateGroup(element);
            if (element.PartyKey != null)
            {
                writer.WriteAttributeString("PartyKey", XmlNS, element.PartyKey.ToString());
            }

            if (element.PartyKeyRef != null)
            {
                writer.WriteAttributeString("PartyKeyRef", XmlNS, element.PartyKeyRef.ToString());
            }

            xLinkWriter.WriteXLink(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteLanguageCodeGroup(element);
            ctWriter.WriteExtendedAttributes(element);
        }

        public void WritePartyChildren(PartyType element)
        {
            if (element == null)
            {
                return;
            }

            WriteFreeTextLines(element.FreeTextLines);
            xnlWriter.WritePartyName(element.PartyName, "PartyName", XmlNS);
            WriteAddresses(element.Addresses);
            WriteAccounts(element.Accounts);
            WriteContactNumbers(element.ContactNumbers);
            WriteDocuments(element.Documents);
            WriteElectronicAddressIdentifiers(element.ElectronicAddressIdentifiers);
            WriteEvents(element.Events);
            WriteIdentifiers(element.Identifiers);
            WriteMemberships(element.Memberships);
            WriteRelationships(element.SimpleRelationships);
            WriteRevenues(element.Revenues);
            WriteStocks(element.Stocks);
            WriteVehicles(element.Vehicles);
            WriteOrganisationInfo(element.OrganisationInfo);
            WritePersonInfo(element.PersonInfo);
            WriteBirthInfo(element.BirthInfo);
            WriteCountriesOfResidence(element.CountriesOfResidence);
            WriteFavourites(element.Favourites);
            WriteHabits(element.Habits);
            WriteHobbies(element.Hobbies);
            WriteLanguages(element.Languages);
            WriteNationalities(element.Nationalities);
            WriteOccupations(element.Occupations);
            WritePhysicalInfo(element.PhysicalInfo);
            WritePersonPreferences(element.Preferences);
            WriteQualifications(element.Qualifications);
            WriteVisas(element.Visas);
        }

        public void WritePartyEndElement(PartyType element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteEndElement();
        }

        /// <summary>
        /// Note at least one person name required
        /// </summary>
        public void WritePersonDetails(PersonDetailsType element)
        {
            if (element == null || element.PersonNames == null || 
                element.PersonNames.Count == 0) // at least one element required
            {
                return;
            }

            writer.WriteStartElement("PersonDetails", XmlNS);
            if (element.Usage != null)
            {
                writer.WriteAttributeString("Usage", XmlNS, element.Usage.ToString());
            }

            if (element.Status != null)
            {
                writer.WriteAttributeString("Status", XmlNS, element.Status.ToString());
            }

            ctWriter.WriteValidityDateGroup(element);
            if (element.PersonDetailsKey != null)
            {
                writer.WriteAttributeString("PersonDetailsKey", XmlNS, element.PersonDetailsKey.ToString());
            }

            if (element.PersonDetailsKeyRef != null)
            {
                writer.WriteAttributeString("PersonDetailsKeyRef", XmlNS, element.PersonDetailsKeyRef.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteLanguageCodeGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            WriteFreeTextLines(element.FreeTextLines);
            if (element.PersonNames != null)
            {
                foreach (var personName in element.PersonNames)
                {
                    xnlWriter.WritePersonName(personName);
                }
            }

            WriteAddresses(element.Addresses);
            WriteAccounts(element.Accounts);
            WriteContactNumbers(element.ContactNumbers);
            WriteDocuments(element.Documents);
            WriteElectronicAddressIdentifiers(element.ElectronicAddressIdentifiers);
            WriteEvents(element.Events);
            WriteIdentifiers(element.Identifiers);
            WriteMemberships(element.Memberships);
            WriteRelationships(element.Relationships);
            WriteRevenues(element.Revenues);
            WriteStocks(element.Stocks);
            WriteVehicles(element.Vehicles);
            WritePersonInfo(element.PersonInfo);
            WriteBirthInfo(element.BirthInfo);
            WriteCountriesOfResidence(element.CountriesOfResidence);
            WriteFavourites(element.Favourites);
            WriteHabits(element.Habits);
            WriteHobbies(element.Hobbies);
            WriteLanguages(element.Languages);
            WriteNationalities(element.Nationalities);
            WriteOccupations(element.Occupations);
            WritePhysicalInfo(element.PhysicalInfo);
            WritePersonPreferences(element.Preferences);
            WriteQualifications(element.Qualifications);
            WriteVisas(element.Visas);
            writer.WriteEndElement();
        }
    }
}
